#pragma once

#include "luca.giona.h"
#include "eratosteneSieve.h"
#include "utils.h"
#include "../common/MathUtils.h"
#include <cmath>
#include <vector>


bool LucaGiona::Init(Register& o_register)
{
	m_name = "LUCA GIONA";

	REGISTER_FUNC(LucaGiona, 1);
    REGISTER_FUNC(LucaGiona, 2);
    REGISTER_FUNC(LucaGiona, 3);
    REGISTER_FUNC(LucaGiona, 4);
    REGISTER_FUNC(LucaGiona, 5);
    REGISTER_FUNC(LucaGiona, 6);
    REGISTER_FUNC(LucaGiona, 7);
    REGISTER_FUNC(LucaGiona, 8);
    REGISTER_FUNC(LucaGiona, 9);
    REGISTER_FUNC(LucaGiona, 10);
//    REGISTER_FUNC(LucaGiona, 11);
//    REGISTER_FUNC(LucaGiona, 12);
//    REGISTER_FUNC(LucaGiona, 13);
    REGISTER_FUNC(LucaGiona, 14);

	return true;
}



RetType LucaGiona::Problem1()
{
    RetType ret = 0;
    UInt32 value = 1000;
    --value;

//ALL 3 MULTIPLES
    UInt32 maxNum = (value / 3);
    ret += (3 * LGFun::GaussSerieSum(maxNum));

//ALL 5 MULTIPLES
    maxNum = (value / 5);
    ret += (5 * LGFun::GaussSerieSum(maxNum));

//ALL 15 MULTIPLES (intercection between 3 and 5)
    maxNum = (value / 15);
    ret -= (15 * LGFun::GaussSerieSum(maxNum));
    
	return ret;
}



RetType LucaGiona::Problem2()
{
	RetType ret = 0;

    UInt32 n = 1;
    UInt32 nn = 2;

    while(nn < 4000000)
    {
        ret += nn;
//Even-valued term is every 3 positions on the sequence
        std::swap(n, nn);
        nn += n;
        std::swap(n, nn);
        nn += n;
        std::swap(n, nn);
        nn += n;
    }

    return ret;
}



RetType LucaGiona::Problem3()
{
    UInt64 value = 600851475143;

    math::FactorExpList list;
    math::ComputePrimeFactors(value, list);

    return static_cast<RetType>(list.back().first);
}



RetType LucaGiona::Problem4()
{
    UInt32 ret = 0;
    UInt32 i, j;
    i = j = 999;
    UInt32 nextMax = i * j; // Stop criterion
    // If I see the problem as a Multiplication Table, I search in the lower tringle.

    while(j > 99 && ret < nextMax)
    {
        UInt32 test = i*j;
		if(math::IsPalindrome(test) && ret < test) 
        {
            ret = test;
            i = 999; //Change column, all other number are smaller.
            --j;
            nextMax = i * j;
            continue;
        }

        // A litle improvment may bo done: change column at the midle, if a result is already if foud.
        if(i == j) //Change column.
        {
            i = 999;
            --j;
            nextMax = i * j;
        }
        else  //Change row.
            --i;
    }

    return static_cast<RetType>(ret);
}



RetType LucaGiona::Problem5()
{
	typedef std::vector<UInt>  NumberList;

	UInt32 value = 20;
    value -= 2;
    NumberList list(value);
    for(UInt32 i = 0; i < value; ++i)
        list[i] = i+2;
    return math::GetMCM(list); //faster!
	//return math::rejected::GetMCM_LG(list);
}

RetType LucaGiona::Problem6()
{
    RetType ret = 0;
    UInt32 value = 100;
    UInt32 sum = LGFun::GaussSerieSum(value);
    UInt32 quad = LGFun::SquareSerieSum(value);

    sum *=sum;
    ret = static_cast<RetType>((sum < quad)? quad - sum : sum - quad);

    return ret;
}



RetType LucaGiona::Problem7()
{
    // sqrt(n) = m, m is a upper bound of count of prime numbers lower then n.
    // Then [0, m*m] is a range were we can find the m^th prime number. I think.
    // Now n become to big in this problem and I utilize a number that is smallet then n. :P
    // A possible solution for a generic value is to use a range = value * k (for examples k = 10)...
    // Launch the eratostene siege (CompuneNPrime) if it return 0 retry with range = range * k.
    UInt32 value = 10001;
    //UInt32 range = 105000; // Magic number. ;P (ugasoft - too magic: it's unused and warningful)

    LGFun::EratosteneSieve primes(105000);
    LGFun::NPrimeNum nPrime(value);
    primes.AddPerPrimeNumTask(&nPrime);
    primes.Compute();
    return static_cast<RetType>(nPrime.GetNPrimeNum());
}



UInt32 MultNextDigit(const UInt8* const i_digits, UInt32 i_num) // For problem 8.
{
    UInt32 ret = (i_digits[0] - '0');
    for(UInt32 i = 1; i < i_num; ++i)
        ret *= (i_digits[i] - '0');
    return ret;
}



RetType LucaGiona::Problem8()
{
    UInt8 digits[] =
"73167176531330624919225119674426574742355349194934\
96983520312774506326239578318016984801869478851843\
85861560789112949495459501737958331952853208805511\
12540698747158523863050715693290963295227443043557\
66896648950445244523161731856403098711121722383113\
62229893423380308135336276614282806444486645238749\
30358907296290491560440772390713810515859307960866\
70172427121883998797908792274921901699720888093776\
65727333001053367881220235421809751254540594752243\
52584907711670556013604839586446706324415722155397\
53697817977846174064955149290862569321978468622482\
83972241375657056057490261407972968652414535100474\
82166370484403199890008895243450658541227588666881\
16427171479924442928230863465674813919123162824586\
17866458359124566529476545682848912883142607690042\
24219022671055626321111109370544217506941658960408\
07198403850962455444362981230987879927244284909188\
84580156166097919133875499200524063689912560717606\
05886116467109405077541002256983155200055935729725\
71636269561882670428252483600823257530420752963450";

    UInt32 i = 0;
    UInt32 maxDigit = 1000;
    maxDigit -= 5;
    RetType ret = 0;
    RetType current = ret;

    for(; i < maxDigit; ++i)
    {
        //UInt8 exitDigit = (digits[i] - '0');
        UInt8 newDigit = (digits[i+5] - '0');

        if(!newDigit)  //simple jump.
        {
            i += 5;
            continue; // i+6 and check.
        }

        current = MultNextDigit(&digits[i], 5);
        if(ret < current)
            ret = current;
    }

    return ret;
}



RetType LucaGiona::Problem9()
{
    Int32 a = 1;
    Int32 b = 2;
    Int32 c = 997;
    Int32 sum = 1000;

    Int32 aMax = sum / 3;
    Int32 bMax = 0;
    for(a = 1; a < aMax; ++a)
    {
        bMax = sum - a;
        bMax /= 2;
        for(b = a+1; b < bMax; ++b)
        {
            c = (1000 - (a + b));
            if(!(b < c))
                continue;
            
            Int64 pows = (a*a) + (b*b) - (c*c);
            if(pows == 0)
                return (a*b*c);
        }
    }
    return 0;
}



RetType LucaGiona::Problem10()
{
    UInt32 value = 2000000;

    LGFun::EratosteneSieve primes(value);
    LGFun::PrimeNumSum sum;
    primes.AddPerPrimeNumTask(&sum);
    primes.Compute();

    return static_cast<RetType>(sum.GetSum());
}



UInt32 HailstoneSequence(UInt32 i_number)
{
    if(math::IsOdd(i_number)) return ((3 * i_number) + 1);
    else return i_number >> 1;
} 



RetType LucaGiona::Problem14()
{
    UInt32 value = 1000000;

/* Good mode
    typedef std::pair<UInt32, UInt32> NumCount;

    NumCount max(1,0);
    NumCount number(0,0);
    UInt32 temp = 0;

    for(UInt32 i = value; i > 2; --i)
    {
        number.first = i;
        number.second = 0;

        while(number.first > 1)
        {
            if(LGFun::IsOdd(number.first))
            { 
                number.first *= 3; 
                ++number.first; // Now number.first always is even.
                number.first <<= 1;
                number.second += 2;
            }
            else
            {
                number.first >>= 1;
                ++number.second;
            }
        }

        return max.first;
End of good mode */

/* Evil mode */
    UInt32 evilNum = 0; // Current number evaluated.
    UInt32 evilTemp = 0; // Temporary number.
    UInt32 evilCount = 0; // Current counter.
    UInt32 evilMaxNum = 0; // Maximum number.
    UInt32 evilMaxCount = 0; // Maximum counter.

    for(UInt32 i = value; i > 2; --i)
    {        
// Can I skip some value?
        evilNum = i;
        evilCount = 0;

        for(;;) // :O
        {
            while(!(evilNum & 1)) // While is even...
            {
                evilNum >>= 1;
                ++evilCount;
            }

            if(evilNum == 1) // Stop condition.
                break;

            evilTemp = evilNum;
            evilNum <<= 1; // 2n
            evilNum |= 1; // 2n + 1
            evilNum += evilTemp; // 2n + 1 + n = 3n + 1   :O
            ++evilCount;
        }

        if(evilMaxCount < evilCount)
        {
            evilMaxNum = i;
            evilMaxCount = evilCount;
        }
    }

    return evilMaxNum;
/*End of evil mode */
}
